/**
 * Keyboard.cpp
 * Copyright _ 2001 Li Zhaoming. All rights reserved.
 */

#include "stdafx.h"
#include "resource.h"

#define IDM_HIDE		301
#define IDM_ABOUT		302

HWND ghwnd;
HHOOK hhkMouse;
bool gbShift = false;
bool gbControl = false;
bool gbMenu = false;
WNDPROC wpOriginButtonProc;
LRESULT MouseProc(int nCode,WPARAM wparam,LPARAM lparam);

// Main window message table definition.
MSD rgmsd[] =
{
	{WM_INITDIALOG,		msgCreate		},
	{WM_MOUSEMOVE,		msgMouseMove	},
	{WM_RBUTTONDOWN,	msgMouseRDown	},
	{WM_COMMAND,		msgCommand		},
	{WM_SYSCOMMAND,		msgSysCommand	},
	{WM_DESTROY,		msgDestroy		},
	{WM_CLOSE,			msgDestroy		},
};

MSDI msdiMain =
{
	sizeof(rgmsd) / sizeof(MSD),
	rgmsd,
	edwpNone
};

// Virtual key code
typedef struct _KEYCODE
{
	UINT ID;
	BYTE VK;
} KEYCODE;

KEYCODE keycode[] =
{
	{IDC_KEY_BACK,		0x08},
	{IDC_KEY_TAB,		0x09},

	{IDC_KEY_ENTER,		0x0D},

	{IDC_KEY_SHIFT,		0x10},
	{IDC_KEY_CONTROL,	0x11},
	{IDC_KEY_MENU,		0x12},
	{IDC_KEY_PAUSE,		0x13},
	{IDC_KEY_CAPS,		0x14},

	{IDC_KEY_ESC,		0x1B},

	{IDC_KEY_SPACE,		0x20},
	{IDC_KEY_PAGEUP,	0x21},
	{IDC_KEY_PAGEDOWN,	0x22},
	{IDC_KEY_END,		0x23},
	{IDC_KEY_HOME,		0x24},
	{IDC_KEY_LEFT,		0x25},
	{IDC_KEY_UP,		0x26},
	{IDC_KEY_RIGHT,		0x27},
	{IDC_KEY_DOWN,		0x28},

	{IDC_KEY_INS,		0x2D},
	{IDC_KEY_DEL,		0x2E},

	{IDC_KEY_0,			0x30},
	{IDC_KEY_1,			0x31},
	{IDC_KEY_2,			0x32},
	{IDC_KEY_3,			0x33},
	{IDC_KEY_4,			0x34},
	{IDC_KEY_5,			0x35},
	{IDC_KEY_6,			0x36},
	{IDC_KEY_7,			0x37},
	{IDC_KEY_8,			0x38},
	{IDC_KEY_9,			0x39},
	
	{IDC_KEY_A,			0x41},
	{IDC_KEY_B,			0x42},
	{IDC_KEY_C,			0x43},
	{IDC_KEY_D,			0x44},
	{IDC_KEY_E,			0x45},
	{IDC_KEY_F,			0x46},
	{IDC_KEY_G,			0x47},
	{IDC_KEY_H,			0x48},
	{IDC_KEY_I,			0x49},
	{IDC_KEY_J,			0x4A},
	{IDC_KEY_K,			0x4B},
	{IDC_KEY_L,			0x4C},
	{IDC_KEY_M,			0x4D},
	{IDC_KEY_N,			0x4E},
	{IDC_KEY_O,			0x4F},
	{IDC_KEY_P,			0x50},
	{IDC_KEY_Q,			0x51},
	{IDC_KEY_R,			0x52},
	{IDC_KEY_S,			0x53},
	{IDC_KEY_T,			0x54},
	{IDC_KEY_U,			0x55},
	{IDC_KEY_V,			0x56},
	{IDC_KEY_W,			0x57},
	{IDC_KEY_X,			0x58},
	{IDC_KEY_Y,			0x59},
	{IDC_KEY_Z,			0x5A},
	{IDC_KEY_WIN,		0x5B},
	{IDC_KEY_APP,		0x5D},

	{IDC_KEY_F1,		0x70},
	{IDC_KEY_F2,		0x71},
	{IDC_KEY_F3,		0x72},
	{IDC_KEY_F4,		0x73},
	{IDC_KEY_F5,		0x74},
	{IDC_KEY_F6,		0x75},
	{IDC_KEY_F7,		0x76},
	{IDC_KEY_F8,		0x77},
	{IDC_KEY_F9,		0x78},
	{IDC_KEY_F10,		0x79},
	{IDC_KEY_F11,		0x7A},
	{IDC_KEY_F12,		0x7B},

	{IDC_KEY_COLON,		0xBA},
	{IDC_KEY_PLUS,		0xBB},
	{IDC_KEY_COMMA,		0xBC},
	{IDC_KEY_PERIOD,	0xBE},
	{IDC_KEY_MINUS,		0xBD},
	{IDC_KEY_SLASH,		0xBF},
	{IDC_KEY_WAVE,		0xC0},

	{IDC_KEY_BRA,		0xDB},
	{IDC_KEY_SEPARATOR,	0xDC},
	{IDC_KEY_KET,		0xDD},
	{IDC_KEY_QUOTE,		0xDE},
};


/**
 * Processes messages.
 */
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
	return dispatchMessage(&msdiMain, hwnd, uMessage, wparam, lparam);
}

/**
 * Handle the WM_COMMAND messages.
 */
LRESULT msgCommand(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
	for (int i = 0; i < sizeof(keycode)/sizeof(KEYCODE); i++)
	{
		UINT id = keycode[i].ID;
		BYTE vk = keycode[i].VK;
		if (LOWORD(wparam) == id)
		{
			switch (keycode[i].ID)
			{
			case IDC_KEY_SHIFT:
				gbShift = !gbShift;
				break;
			case IDC_KEY_CONTROL:
				gbControl = !gbControl;
				break;
			case IDC_KEY_MENU:
				gbMenu = !gbMenu;
				break;
			default:
				if (gbShift)
					keybd_event(VK_SHIFT, 0, 0, 0);
				if (gbControl)
					keybd_event(VK_CONTROL, 0, 0, 0);
				if (gbMenu)
					keybd_event(VK_MENU, 0, 0, 0);
				// Key stroke
				keybd_event(vk, 0, 0, 0);
				keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);

				if (gbShift)
				{
					keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
					gbShift = false;
					InvalidateRect(GetDlgItem(hwnd, IDC_KEY_SHIFT), NULL, true);
				}
				if (gbControl)
				{
					keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
					gbControl = false;
					InvalidateRect(GetDlgItem(hwnd, IDC_KEY_CONTROL), NULL, true);
				}
				if (gbMenu)
				{
					keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
					gbMenu = false;
					InvalidateRect(GetDlgItem(hwnd, IDC_KEY_MENU), NULL, true);
				}
				break;
			}
		}
	}
	return 0;
}

/**
 * WM_INITDIALOG
 */
LRESULT msgCreate(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
	RECT rc;
	HMENU hmenu;
	ghwnd = hwnd;
	for (int i = 0; i < sizeof(keycode)/sizeof(KEYCODE); i++)
	{
		wpOriginButtonProc = (WNDPROC) 
		SetWindowLong(GetDlgItem(hwnd, keycode[i].ID), GWL_WNDPROC, (long) buttonSubclassProc);
	}
	// Set Icon.
	SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) LoadIcon(g_hinstance, (LPCTSTR)MAKEINTRESOURCE(IDI_APPICON)));
	// System Menu
	hmenu = GetSystemMenu(hwnd, FALSE);
	AppendMenu (hmenu, MF_SEPARATOR, 0,             NULL) ;
	AppendMenu (hmenu, 0,            IDM_HIDE,		"Hide &Keyboard");
	AppendMenu (hmenu, MF_SEPARATOR, 0,             NULL) ;
	AppendMenu (hmenu, 0,            IDM_ABOUT,     "&About...") ;
	// Title
	SetWindowText(hwnd, szTitle);
	// Position.
	GetClientRect(hwnd, &rc);
	SetWindowPos(hwnd, NULL,
		GetSystemMetrics(SM_CXFULLSCREEN) - (rc.right - rc.left),
		GetSystemMetrics(SM_CYFULLSCREEN) - (rc.bottom - rc.top),
		0, 0, SWP_NOSIZE | SWP_NOZORDER);
	// Mouse hook.
    if (!(hhkMouse = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)MouseProc, g_hinstance, 0)))
		return FALSE;
	return 0;				
}

/**
 *
 */
LRESULT msgMouseMove(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
	// If dragging
	if (wparam & MK_LBUTTON)
	{
		PostMessage (hwnd, WM_NCLBUTTONDOWN, HTCAPTION, lparam);	
	}
	return 0;
}

/**
 *
 */
LRESULT msgMouseRDown(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
	int id;
	POINT point;
	point.x = LOWORD(lparam);
	point.y = HIWORD(lparam);
	// Convert the mouse point to screen coordinates since that is what
	// TrackPopup expects.
	ClientToScreen (hwnd, (LPPOINT)&point);
	id = TrackPopupMenu (GetSystemMenu(hwnd, FALSE), TPM_LEFTALIGN | TPM_RETURNCMD |TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, 0, hwnd, NULL);
	SendMessage (hwnd, WM_SYSCOMMAND, id, lparam);
	return 0;
}

/**
 *
 */
LRESULT msgSysCommand(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
	switch (LOWORD(wparam))
	{
	case IDM_ABOUT:
		// About box.
		MessageBox(hwnd, "Soft Keyboard v1.0\nCopyright _ 2001 Li Zhaoming.\nAll Rights Reserved.", szAppName, MB_OK | MB_ICONINFORMATION);
		break;
	case SC_MINIMIZE:
	case SC_RESTORE:
	case IDM_HIDE:
		if (IsIconic(hwnd))
		{
			//SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
			ShowWindow(hwnd, SW_NORMAL);
			ModifyMenu(GetSystemMenu(hwnd, false), IDM_HIDE, MF_BYCOMMAND | MF_STRING, IDM_HIDE, "Hide &Keyboard");

		}
		else
		{
			//SendMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
			ShowWindow(hwnd, SW_MINIMIZE);
			CheckMenuItem(GetSystemMenu(hwnd, false), IDM_HIDE, MF_CHECKED);
			ModifyMenu(GetSystemMenu(hwnd, false), IDM_HIDE, MF_BYCOMMAND | MF_STRING, IDM_HIDE, "Show &Keyboard");
		}
		break;
	}
	return 0;
}

/**
 * Calls PostQuitMessage().
 */
LRESULT msgDestroy(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
	// Should call SetWindowLong(HWND, GWL_WNDPROC, WNDPROC) here, 
	// However, it's safe not to do so.
	// Release hook.
	UnhookWindowsHookEx(hhkMouse);
	// Quit
	PostQuitMessage(0);
	return 0;
}

/**
 *
 */
void releaseFocus()
{
	if (IsWindow(gFocus))
	{
		HWND hwnd = GetForegroundWindow();
		if (IsWindow(hwnd))
		{
			if(hwnd == gFocus)
			{
				return;
			}
		}
		SetForegroundWindow(gFocus);
	}
}

/**
 *
 */
LRESULT CALLBACK buttonSubclassProc(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    RECT rc;
	PAINTSTRUCT ps;
	GetClientRect(hwnd, &rc);
	HDC hdc;
	char buffer[100];
	DWORD cmd;

	switch (uMessage)
	{
	case WM_PAINT:
		hdc=BeginPaint(hwnd, &ps);
		FillRect(hdc, &rc, (HBRUSH) GetStockObject(LTGRAY_BRUSH));

		GetWindowText(hwnd, buffer, sizeof(buffer));
		SelectObject(hdc, (HFONT) SendMessage(hwnd, WM_GETFONT, 0, 0L));
		SetBkMode(hdc,TRANSPARENT);
		DrawText(hdc, buffer, strlen(buffer), &rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);

		FrameRect(hdc, &rc, (HBRUSH) GetStockObject(BLACK_BRUSH));
		InflateRect(&rc, -1, -1);
		FrameRect(hdc, &rc, (HBRUSH) GetStockObject(WHITE_BRUSH));
		InflateRect(&rc, 1, 1);
		cmd = GetWindowLong(hwnd, GWL_ID);
		if ((cmd == IDC_KEY_SHIFT && gbShift) ||
			(cmd == IDC_KEY_CONTROL && gbControl) ||
			(cmd == IDC_KEY_MENU && gbMenu))
		{
			rc.left++;
			rc.top++;
		}
		else
		{
			rc.bottom--;
			rc.right--;
		}
		FrameRect(hdc, &rc, (HBRUSH) GetStockObject(GRAY_BRUSH));
		// End paint.
		EndPaint(hwnd, &ps);
		break;
	case WM_LBUTTONDOWN:
	case WM_LBUTTONDBLCLK:
		hdc = GetDC(hwnd);
		ValidateRect(hwnd, &rc);
		FrameRect(hdc, &rc, (HBRUSH) GetStockObject(BLACK_BRUSH));
		InflateRect(&rc, -1, -1);
		FrameRect(hdc, &rc, (HBRUSH) GetStockObject(WHITE_BRUSH));
		InflateRect(&rc, 1, 1);
		rc.left++;
		rc.top++;
		FrameRect(hdc, &rc, (HBRUSH) GetStockObject(GRAY_BRUSH));
		ReleaseDC(hwnd, hdc);
		SetCapture(hwnd);
		break;
	case WM_LBUTTONUP:
		releaseFocus();
		SendMessage(GetParent(hwnd), WM_COMMAND, GetWindowLong(hwnd, GWL_ID), 0);
		InvalidateRect(hwnd, NULL, TRUE);
		ReleaseCapture();
		break;
	default:
		return CallWindowProc(wpOriginButtonProc, hwnd, uMessage, wparam, lparam); 
	}
	return 0;
}

/**
 *
 */
LRESULT MouseProc(int nCode,WPARAM wparam,LPARAM lparam)
{
    int i;
	LPMOUSEHOOKSTRUCT s = (LPMOUSEHOOKSTRUCT) lparam;

    if (nCode < 0)  // do not process the message 
		return CallNextHookEx(NULL, nCode, wparam, lparam); 
	
	switch (wparam)
	{
	case WM_LBUTTONDBLCLK:
	case WM_LBUTTONDOWN:
		for (i = 0; i < sizeof(keycode)/sizeof(KEYCODE); i++)
			if (GetDlgItem(ghwnd, keycode[i].ID) == s->hwnd)
			{
				return PostMessage(GetDlgItem(ghwnd, keycode[i].ID),wparam, 0, 0);
			}
		break;
	case WM_LBUTTONUP:
		for (i = 0; i < sizeof(keycode)/sizeof(KEYCODE); i++)
			if (GetDlgItem(ghwnd, keycode[i].ID) == s->hwnd)
			{
				return PostMessage(GetDlgItem(ghwnd, keycode[i].ID), wparam, 0, 0);
			}
		break;
	}
	return CallNextHookEx(NULL, nCode, wparam, lparam);
}
